﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Linq;
using System.Text;
using System.Web.Security;
using NUnit.Framework;
using Secure.Client;

namespace Secure.Tests.Client
{
    [TestFixture]
    public class SimpleRoleProviderFixture
    {
        [Test]
        public void should_instantiate_the_role_provider()
        {
            RoleProvider provider = CreateAndInitializeProvider();

            Assert.AreEqual("TestFixture", provider.ApplicationName);
        }

        [Test]
        public void should_initialize_the_role_provider_with_some_roles()
        {
            RoleProvider provider = CreateAndInitializeProvider();
            provider.AddUsersToRoles(new[] {"david"}, new [] {"user"});

            Assert.IsTrue(provider.IsUserInRole("david", "user"));
            Assert.IsFalse(provider.IsUserInRole("david", "admin"));

            provider.AddUsersToRoles(new[] { "david" }, new[] { "admin" });

            Assert.IsTrue(provider.IsUserInRole("david", "admin"));

        }

        [Test]
        public void should_ensure_a_user_is_correctly_removed_from_a_role()
        {
            RoleProvider provider = CreateAndInitializeProvider();
            provider.AddUsersToRoles(new[] { "david" }, new[] { "user" });
            provider.AddUsersToRoles(new[] { "david" }, new[] { "admin" });

            Assert.IsTrue(provider.IsUserInRole("david", "user"));
            Assert.IsTrue(provider.IsUserInRole("david", "admin"));

            provider.RemoveUsersFromRoles(new[] { "david" }, new[] { "user" });

            Assert.IsFalse(provider.IsUserInRole("david", "user"));
        }

        [Test]
        public void should_find_all_matching_users_in_a_role()
        {
            RoleProvider provider = CreateAndInitializeProvider();
            provider.AddUsersToRoles(new[] {"david", "derrick", "mary"}, new[] {"user"});
            provider.AddUsersToRoles(new[] {"david"}, new[] {"admin"});

            string[] usersWithA_D = provider.FindUsersInRole("user", "d");
            string[] usersWithAnM = provider.FindUsersInRole("user", "m");
            string[] usersWithAnR = provider.FindUsersInRole("user", "r");

            Assert.AreEqual(new [] {"david", "derrick"}, usersWithA_D);
            Assert.AreEqual(new[] {"mary"}, usersWithAnM);
            Assert.AreEqual(new[] { "derrick", "mary" }, usersWithAnR);
        }

        [Test]
        public void should_get_all_roles_for_a_user()
        {
            RoleProvider provider = CreateAndInitializeProvider();
            provider.AddUsersToRoles(new[] {"david", "derrick", "mary"}, new[] {"user"});
            provider.AddUsersToRoles(new[] {"david"}, new[] {"admin"});

            string[] rolesForDavid = provider.GetRolesForUser("david");
            string[] rolesForDerrick = provider.GetRolesForUser("derrick");
            string[] rolesForMary = provider.GetRolesForUser("mary");

            Assert.AreEqual(new[] {"user", "admin"}, rolesForDavid);
            Assert.AreEqual(new[] {"user"}, rolesForDerrick);
            Assert.AreEqual(new[] {"user"}, rolesForMary);
        }

        [Test]
        public void should_test_whether_a_role_exists()
        {
            RoleProvider provider = CreateAndInitializeProvider();

            Assert.IsFalse(provider.RoleExists("user"));
            Assert.IsFalse(provider.RoleExists("admin"));

            provider.CreateRole("user");

            Assert.IsTrue(provider.RoleExists("user"));

            provider.AddUsersToRoles(new[] { "david", "derrick", "mary" }, new[] { "user" });
            provider.AddUsersToRoles(new[] { "david" }, new[] { "admin" });

            Assert.IsTrue(provider.RoleExists("admin"));
        }

        [Test]
        public void should_remove_a_role()
        {
            RoleProvider provider = CreateAndInitializeProvider();
            provider.CreateRole("user");

            Assert.IsTrue(provider.RoleExists("user"));

            provider.DeleteRole("user", false);

            Assert.IsFalse(provider.RoleExists("user"));
        }

        private RoleProvider CreateAndInitializeProvider()
        {
            RoleProvider provider = new SimpleRoleProvider();
            NameValueCollection settings = new NameValueCollection();

            provider.Initialize("TestFixture", settings);

            return provider;
        }
    }


}

namespace Secure.Client
{
    public sealed class SimpleRoleProvider : RoleProvider
    {
        private string _applicationName;
        private readonly Dictionary<string, List<string>> _roles = new Dictionary<string, List<string>>();

        public override string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            _applicationName = name;
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            if (_roles.ContainsKey(roleName))
            {
                return _roles[roleName].Contains(username);
            }

            return false;
        }

        public override string[] GetRolesForUser(string username)
        {
            List<string> roles = new List<string>();
            foreach (KeyValuePair<string, List<string>> role in _roles)
            {
                if (role.Value.Contains(username))
                {
                    roles.Add(role.Key);
                }
            }

            return roles.ToArray();
        }

        public override void CreateRole(string roleName)
        {
            if (!_roles.ContainsKey(roleName))
            {
                _roles.Add(roleName, new List<string>());
            }
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            if (throwOnPopulatedRole && _roles.Count > 0)
            {
                throw new ProviderException("Cannot delete a populated role.");
            }

            if (_roles.ContainsKey(roleName))
            {
                _roles.Remove(roleName);

                return true;
            }

            return false;
        }

        public override bool RoleExists(string roleName)
        {
            return _roles.ContainsKey(roleName);
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            foreach (string role in roleNames)
            {
                List<string> users;
                if (!_roles.TryGetValue(role, out users))
                {
                    users = new List<string>();
                    _roles.Add(role, users);
                }

                foreach (string username in usernames)
                {
                    if (!users.Contains(username))
                    {
                        users.Add(username);
                    }
                }
            }
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            foreach (string role in roleNames)
            {
                List<string> users;
                if (!_roles.TryGetValue(role, out users))
                {
                    users = new List<string>();
                }

                foreach (string username in usernames)
                {
                    if (users.Contains(username))
                    {
                        users.Remove(username);
                    }
                }
            }
        }

        public override string[] GetUsersInRole(string roleName)
        {
            List<string> users;
            _roles.TryGetValue(roleName, out users);

            return users.ToArray();
        }

        public override string[] GetAllRoles()
        {
            return _roles.Keys.ToArray();
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            List<string> users;
            _roles.TryGetValue(roleName, out users);

            return users.FindAll(user => user.Contains(usernameToMatch)).ToArray();
        }
    }
}